package com.company.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * @description:
 * @author: zgw
 * @create: 2021-03-12 15:08
 **/
public class GroupChatServer {

    private Selector selector;

    private ServerSocketChannel serverSocketChannel;

    public static final int PORT = 6667;

    //构造器初始化成员变量
    public GroupChatServer(){
        try {
            //打开一个选择器
            this.selector = Selector.open();
            //打开一个通道
            this.serverSocketChannel = ServerSocketChannel.open();
            //绑定地址，端口号
            this.serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",PORT));
            //设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            //把通道注册到选择器种
            /**
             * 读事件
             * public static final int OP_READ = 1 << 0; //2^0=1
             * 写事件
             * public static final int OP_WRITE = 1 << 2; // 2^2=4
             * 连接操作,Client端支持的一种操作
             * public static final int OP_CONNECT = 1 << 3; // 2^3=8
             * 连接可接受操作,仅ServerSocketChannel支持
             *public static final int OP_ACCEPT = 1 << 4; // 2^4=16
             */
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 监听,并且接收客户端消息，并转发到其他客户端
     */
    public void listen(){

        try {
            while (true){
                //获取监听事件总数
                int count = selector.select(2000);
                if (count>0){
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    //获取选择建集合
                    Iterator<SelectionKey> it = selectionKeys.iterator();
                    while (it.hasNext()){
                        SelectionKey key = it.next();
                        //如果是获取连接事件
                        if (key.isAcceptable()){
                            SocketChannel socketChannel = serverSocketChannel.accept();
                            //设置为非阻塞
                            socketChannel.configureBlocking(false);
                            //注册到选择器种
                            socketChannel.register(selector,SelectionKey.OP_READ);
                            System.out.println(socketChannel.getRemoteAddress()+"上线了");
                        }
                        //如果是读就绪事件
                        if (key.isReadable()){
                            //读取消息,并且转发到其他客户端
                            readData(key);
                        }
                        it.remove();
                    }
                }else {
                    System.out.println("等待...");
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //获取客户端发送过来的消息
    private void readData(SelectionKey key) {
        SocketChannel socketChannel = null;
        try {
            //从selectionKey中获取channel
            socketChannel = (SocketChannel) key.channel();
            //创建一个缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            //把通道中的数据写入缓冲去
            int count = socketChannel.read(byteBuffer);
            //count大于0表示读取到了数据
            if (count>0){
                //把缓存区的byte[] 转成字符串
                String msg = new String(byteBuffer.array());

                System.out.println("from 客户端："+msg);
                //转发到其他客户端
                notifyAllClient(msg,socketChannel);

            }
        } catch (Exception e) {
            try {
                //打印离线通知
                System.out.println(socketChannel.getRemoteAddress()+"离线了。。。");
                //取消注册
                key.cancel();
                //关闭流
                socketChannel.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * 转发到其他客户端
     * @param msg
     * @param noSocketChannel 不需要通知的Channel
     */
    private void notifyAllClient(String msg, SocketChannel noSocketChannel) {
        System.out.println("服务器转发消息~");
        for (SelectionKey selectionKey:selector.keys()){
            Channel channel = selectionKey.channel();
            //channel的类型实际类型是SocketChannel，并且排除不需要通知的通道
            if (channel instanceof SocketChannel && channel != noSocketChannel){
                SocketChannel socketChannel = (SocketChannel) channel;
                //通过消息，包裹获取一个缓冲区
                ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
                try {
                    socketChannel.write(byteBuffer);
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }
    }

    public static void main(String[] args) throws Exception {
        GroupChatServer chatServer = new GroupChatServer();
        //启动服务器，监听
        chatServer.listen();
    }

}
